﻿
using System;
using System.Drawing;

using GECS.Core;

namespace GECS.Util {

	/// <summary>
	/// Some of the utilities needed by the engine. Note that all the utility methods
	/// are static. This class is necessary for the engine to run.
	/// </summary>
    public class GUtil {

        #region variables

        private static Random rand = new Random();

        #endregion

        // Direction and velocities

        /// <summary>
        /// Turns the angle positive.
        /// </summary>
        /// <param name="angle">The negative angle.</param>
        /// <returns>The positive angle.</returns>
        public static int GetPositiveAngle(int angle) {
            if (angle < 0) {
                return angle + 360;
            } else if (angle > 360) {
                return angle - 360;
            } else {
                return angle;
            }
        }

        /// <summary>
        /// Calculates horizontal velocity from the common velocity and inclination.
        /// </summary>
        /// <param name="velocity">The velocity.</param>
        /// <param name="angle">The angle (in degrees).</param>
        /// <returns>Calculated horizontal velocity.</returns>
        public static float GetVelocityX(float velocity, int angle) {
            return (float)(velocity * Math.Sin(Math.PI * GetPositiveAngle(angle) / 180.0));
        }

        /// <summary>
        /// Calculates vertical velocity from the common velocity and inclination.
        /// </summary>
        /// <param name="velocity">The velocity.</param>
        /// <param name="angle">The angle (in degrees).</param>
        /// <returns>Calculated vertical velocity.</returns>
        public static float GetVelocityY(float velocity, int angle) {
            return (float)(velocity * Math.Cos(Math.PI * GetPositiveAngle(angle) / 180.0 )* (-1));
        }

        // Collision Detection

        /// <summary>
        /// Checks if two rectangles intersect each other at a high speed than the
        /// Rectangle class. This is also accurate since the collision detection is
        /// done in four stages.
        /// </summary>
        /// <param name="x1">The upper-left x-coordinate of the first rectangle.</param>
        /// <param name="y1">The upper-left y-coordinate of the first rectangle.</param>
        /// <param name="w1">The width of the first rectangle.</param>
        /// <param name="h1">The height of the first rectangle.</param>
        /// <param name="x2">The upper-left x-coordinate of the second rectangle.</param>
        /// <param name="y2">The upper-left y-coordinate of the second rectangle.</param>
        /// <param name="w2">The width of the second rectangle.</param>
        /// <param name="h2">The height of the second rectangle.</param>
        /// <returns>True if the two rectangles intersect each other.</returns>
        public static bool IsCollision(float x1, float y1, float w1, float h1, float x2, float y2, float w2, float h2) {
            if (x1 < x2 + w2) {
                if (x2 < x1 + w1) {
                    if (y1 < y2 + h2) {
                        if (y2 < y1 + h1) {
                            return true;
                        }
                    }
                }
            }
            return false;
        }

        /// <summary>
        /// Check's for pixel-perfect collision between two images.
        /// </summary>
        /// <param name="x1">The x-position of the first object.</param>
        /// <param name="y1">The y-position of the first object.</param>
        /// <param name="image1">The image of first object.</param>
        /// <param name="x2">The x-position of the second object.</param>
        /// <param name="y2">The y-position of the second object.</param>
        /// <param name="image2">The image of second object.</param>
        /// <returns>True if a collision has been found.</returns>
        public static bool IsPixelPerfectCollision(float x1, float y1, Image image1, float x2, float y2, Image image2) {
            double width1 = x1 + image1.Width - 1, height1 = y1
                            + image1.Height - 1, width2 = x2 + image2.Height - 1, height2 = y2
                                    + image2.Height - 1;
            int xstart = (int) Math.Max(x1, x2), ystart = (int) Math.Max(y1, y2), xend = (int) Math.Min(width1, width2), yend = (int) Math.Min(height1, height2);
            int toty = Math.Abs(yend - ystart);
            int totx = Math.Abs(xend - xstart);

            Bitmap bmp1 = (Bitmap)image1;
            Bitmap bmp2 = (Bitmap)image2;
            for (int y = 1; y < toty - 1; y++) {
                int ny = Math.Abs(ystart - (int) y1) + y;
                int ny1 = Math.Abs(ystart - (int) y2) + y;
                for (int x = 1; x < totx - 1; x++) {
                    int nx = Math.Abs(xstart - (int) x1) + x;
                    int nx1 = Math.Abs(xstart - (int) x2) + x;
                    if ((bmp1.GetPixel(nx, ny).A != 0) && (bmp2.GetPixel(nx1, ny1).A !=0)) {
                        return true;
                    }
                }
            }
            return false;
        }

        // File I/O

        /// <summary>
        /// Loads an external file and returns all the lines as a string array.
        /// </summary>
        /// <param name="fname">The name of the file.</param>
        /// <returns>The string[] array containing all the lines in the file.</returns>
        public static string[] LoadLinesFromFile(string fname) {
            return System.IO.File.ReadAllLines(fname);
        }

        /// <summary>
        /// Loads an embedded resource and returns all the lines as a string array.
        /// </summary>
        /// <param name="fname">The file name.</param>
        /// <returns>The string[] array containing all the lines.</returns>
        public static string[] LoadLinesFromResource(string fname) {
            var reader = new System.IO.StreamReader(Game.GetResourceStream(fname));
            var lines = new System.Collections.Generic.List<string>();
            string line = "";
            while ((line=reader.ReadLine()) != null) {
                lines.Add(line);
            }
            return lines.ToArray();
        }

        // Some real-valued functions for advanced users

        /// <summary>
        /// Returns a random real number between 0 and x. The number is always
        /// smaller than x.
        /// </summary>
        /// <param name="x">The maximum range.</param>
        /// <returns>A random real number.</returns>
        public static int Random(int x) {
            return rand.Next(x);
        }

        /// <summary>
        /// Returns a random real number between x1 (inclusive) and x2 (exclusive).
        /// </summary>
        /// <param name="x1">The inclusive.</param>
        /// <param name="x2">The exclusive.</param>
        /// <returns>A random number between x1 and x2</returns>
        public static int RandomRange(int x1, int x2) {
            return rand.Next(x1, x2);
        }

        /// <summary>
        /// Returns one of the arguments chosen randomly.
        /// </summary>
        /// <param name="values">The array containing values.</param>
        /// <returns>A random value present in the array</returns>
        public static int Choose(int[] values) {
            return values[Random(values.Length + 1)];
        }

        /// <summary>
        /// Returns the fractional part of x, that is, the part behind the decimal dot.
        /// </summary>
        /// <param name="x">The real number.</param>
        /// <returns>The fractional part of x.</returns>
        public static int Frac(double x) {
            string str = "" + x;
            string result = "";
            bool _add = false;
            for (int i = 0; i < str.Length; i++) {
                if (_add) {
            		result += str[i];
                }
            	if (!_add && str[i] == '.') {
                    _add = true;
                }
            }
            return int.Parse(result);
        }
        
        /// <summary>
        /// Returns x to the power n.
        /// </summary>
        /// <param name="x">The base.</param>
        /// <param name="n">The exponent.</param>
        /// <returns>x to the power of n.</returns>
        public static int Power(int x, int n){
        	return (int)Math.Pow(x, n);
        }
        
        /// <summary>
        /// Returns the average of the values.
        /// </summary>
        /// <param name="values">The array of integers.</param>
        /// <returns>The mean of the values.</returns>
        public static int Mean(int[] values){
        	int result = 0;
        	foreach (int i in values){
        		result += i;
        	}
        	result /= values.Length;
        	return result;
        }
        
        /// <summary>
        /// Returns the distance between point (x1,y1) and point (x2,y2).
        /// </summary>
        /// <param name="x1">The abscissa of first point.</param>
        /// <param name="y1">The ordinate of first point.</param>
        /// <param name="x2">The abscissa of second point.</param>
        /// <param name="y2">The ordinate of second point.</param>
        /// <returns>Distance between the two points.</returns>
        public static int PointDistance(int x1, int y1, int x2, int y2){
        	return (int)Math.Sqrt(((x2-x1)*(x2-x1)) + (y2-y1)*(y2-y1));
        }
        
        /// <summary>
        /// You specify the number of sides of a dice which is then thrown. Then if
        /// the dice lands on one, the result is true and the next action is
        /// performed. This can be used to put an element of randomness in your game.
        /// For example, in each step you can generate with a particular chance a
        /// bomb or a change of direction. The larger the number of sides of the
        /// dice, the smaller the chance.
        /// </summary>
        /// <param name="sides">The number of sides of the dice.</param>
        /// <returns>True if the dice lands on one, else false.</returns>
        public static bool Chance(int sides){
        	if (Random(sides+1)==0){
        		return true;
        	}
        	return false;
        }
        
        /// <summary>
        /// Returns the inclination of the line-segment drawn from (x1, y1) to (x2, y2) in degrees.
        /// </summary>
        /// <param name="x1">The abscissa of first point.</param>
        /// <param name="y1">The ordinate of first point.</param>
        /// <param name="x2">The abscissa of second point.</param>
        /// <param name="y2">The ordinate of second point.</param>
        /// <returns>The direction in degrees.</returns>
        public static int GetDirection(float x1, float y1, float x2, float y2){
        	return (int)(Math.PI * Math.Atan((y2-y1)/(x2-x1)) / 180);
        }

    }

}
